﻿// author : David Janacek, dj-soft@seznam.cz, 2013-12-13
// company: Asseco Solution, a.s. CZ
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace Noris.LCS.AIDCCodes.Common
{
    #region class PatternBuffer : buffer pro vkládání řady vzorců (Patternů)
    /// <summary>
    /// PatternBuffer : buffer pro vkládání řady vzorců (Patternů).
    /// Lze přidávat jednotlivé patterny (=stringy), lze číst sumární hodnotu i substringy.
    /// </summary>
    internal class PatternBuffer
    {
        #region Konstrukce
        /// <summary>
        /// Konstruktor. Vytvoří prázdný buffer.
        /// </summary>
        internal PatternBuffer()
        {
            this._BufferString = "";
            this._BufferBuilder = null;
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="buffer">Originální buffer, zde se vytvoří jeho klon</param>
        internal PatternBuffer(PatternBuffer buffer)
        {
            this._BufferString = buffer._BufferString;
            this._BufferBuilder = (buffer._BufferBuilder == null ? (StringBuilder)null : new StringBuilder(buffer._BufferBuilder.ToString()));
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="content">Obsah bufferu</param>
        internal PatternBuffer(string content)
        {
            this._BufferString = content;
            this._BufferBuilder = null;
        }
        /// <summary>
        /// Konstruktor
        /// </summary>
        /// <param name="builder">StringBuilder obsahující data, převezme se jeho obsah (nikoli reference na dodaný objekt)</param>
        internal PatternBuffer(StringBuilder builder)
        {
            this._BufferString = null;
            this._BufferBuilder = (builder == null ? (StringBuilder)null : new StringBuilder(builder.ToString()));
        }
        /// <summary>
        /// Create pattern buffer from string (data), with specified char-order.
        /// Result buffer contain char selected from (data), from indexes and in order specified in (scrambles).
        /// Expamle: data = "ABCDEFGH", scrambles = { 2, 0, 5, 1, 0 }, result = "CAFBA".
        /// </summary>
        /// <param name="data"></param>
        /// <param name="scrambles"></param>
        /// <returns></returns>
        internal static PatternBuffer CreateBufferScrambled(string data, params int[] scrambles)
        {
            PatternBuffer buffer = new PatternBuffer();
            int c = data.Length;
            foreach (int s in scrambles)
            {
                if (s >= 0 && s < c)
                    buffer.Append(data[s]);
            }
            return buffer;
        }
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
        /// <summary>
        /// Textový obsah.
        /// Je NULL, pokud se data právě nyní nacházejí a editují v Builderu.
        /// Není NULL, pokud se data právě nyní nacházejí a editují zde, 
        /// anebo pokud byla naposledy čtena jako String a byla sem přenesena z Builderu.
        /// Vloží se sem NULL po přístupu k Builderu.
        /// </summary>
        private string _BufferString;
        /// <summary>
        /// Buffer.
        /// Je NULL, pokud se data právě nyní nacházejí a editují ve Stringu = pokud byla do Stringu data setována.
        /// Dokud se data ze Stringu jen čtou, pak zde je stále posledně platný Builder, který odpovídá Stringu.
        /// </summary>
        private StringBuilder _BufferBuilder;
        #endregion
        #region Property BufferString a BufferBuilder pro dva základní přístupy k obsahu bufferu
        /// <summary>
        /// Přístup k celému obsahu bufferu ve formě stringu
        /// </summary>
        internal string BufferString
        {
            get
            {
                // Čtení stringu jej v případě potřeby zrekonstruuje ze Builderu.
                // Kopii si uloží do _BufferString, ale Builder neruší (může se hodit).
                // Jakýkoli následný přístup k Builderu (přes get_BufferBuilder()) zruší kopii stringu z _BufferString,
                // protože obsah _BufferBuilder se po jeho get() může změnit!!!
                if (this._BufferString == null)
                {
                    this._BufferString = (this._BufferBuilder == null ? "" : this._BufferBuilder.ToString());

                }
                return this._BufferString;
            }
            set
            {
                // Vložení stringu (který není NULL) uloží string do _BufferString, a zruší Builder:
                // (Builder se zrekonstruuje v případě potřeby v get_BufferBuilder()).
                if (value != null)
                {
                    this._BufferString = value;
                    this._BufferBuilder = null;
                }
            }
        }
        /// <summary>
        /// Přístup k bufferu ve formě Builderu
        /// </summary>
        internal StringBuilder BufferBuilder
        {
            get
            {
                // Čtení builderu jej v případě potřeby zrekonstruuje ze Stringu (pokud nená NULL).
                // Jakmile někdo přistupuje k Builderu, pravděpodobně změní jeho obsah, 
                //  a tak je obsah _BufferString nutno vyNULLovat, a tak je prohlášen za neaktuální.
                if (this._BufferBuilder == null)
                {
                    this._BufferBuilder = (this._BufferString == null ? new StringBuilder() : new StringBuilder(this._BufferString));
                }
                this._BufferString = null;
                return this._BufferBuilder;
            }
            set
            {
                // Vložení Builderu (který není NULL) uloží kopii builderu do _BufferBuilder, a zruší String:
                // (String se zrekonstruuje v případě potřeby v get_BufferString()).
                if (value != null)
                {
                    this._BufferBuilder = new StringBuilder(value.ToString());
                    this._BufferString = null;
                }
            }
        }
        #endregion
        #region Indexer char this[int]
        /// <summary>
        /// Přístup ke znaku na dané pozici (get i set)
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        internal char this[int index]
        {
            get
            {
                if (index < 0 || index >= this.Size)
                    throw new ArgumentOutOfRangeException("index", index, "Index PatternBuffer[index] is out of range: index=" + index.ToString() + "; Size=" + this.Size.ToString());
                if (this._BufferBuilder != null) return this._BufferBuilder[index];
                if (this._BufferString != null) return this._BufferString[index];
                return '\0';
            }
            set
            {
                if (index < 0 || index >= this.Size)
                    throw new ArgumentOutOfRangeException("index", index, "Index PatternBuffer[index] is out of range: index=" + index.ToString() + "; Size=" + this.Size.ToString());
                this.BufferBuilder[index] = value;      // Tady použiju get{} BufferBuilder, který v případě potřeby vytvoří StringBuilder.
            }
        }
        #endregion
        #region Append, override operator + ^
        /// <summary>
        /// Připojí danou hodnotu
        /// </summary>
        /// <param name="bit"></param>
        internal void Append(char bit)
        {
            this.BufferBuilder.Append(bit);
        }
        /// <summary>
        /// Připojí danou hodnotu
        /// </summary>
        /// <param name="patern"></param>
        internal void Append(string patern)
        {
            this.BufferBuilder.Append(patern);
        }
        /// <summary>
        /// Připojí danou číselnou hodnotu převedenou na pattern dané délky
        /// </summary>
        /// <param name="value"></param>
        /// <param name="length"></param>
        internal void Append(int value, int length)
        {
            this.BufferBuilder.Append(CreatePattern(value, length));
        }
        /// <summary>
        /// Připojí obsah dodaného bufferu
        /// </summary>
        /// <param name="buffer"></param>
        internal void Append(PatternBuffer buffer)
        {
            if (buffer != null)
                this.BufferBuilder.Append(buffer.BufferString);
        }
        /// <summary>
        /// Join two buffers into new one buffer: result = a + b
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static PatternBuffer operator +(PatternBuffer a, PatternBuffer b)
        {
            return new PatternBuffer(a.BufferString + b.BufferString);
        }
        /// <summary>
        /// XOR two buffers into new one buffer: result = a XOR b.
        /// Result has length from smaller inputs.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static PatternBuffer operator ^(PatternBuffer a, PatternBuffer b)
        {
            PatternBuffer r = new PatternBuffer();
            int aSize = a.Size;
            int bSize = b.Size;
            int size = (aSize < bSize ? aSize : bSize);
            for (int i = 0; i < size; i++)
                r.Append(_Xor(a[i], b[i]));
            return r;
        }
        private static char _Xor(char a, char b)
        {
            if (a == ' ') return b;
            if (b == ' ') return a;
            if (a == b) return '0';
            return '1';
        }
        #endregion
        #region Úpravy obsahu
        /// <summary>
        /// Provede operaci: this = this XOR buffer
        /// </summary>
        /// <param name="buffer"></param>
        internal void Xor(PatternBuffer buffer)
        {
            if (buffer == null)
                throw new ArgumentNullException("buffer", "Operand buffer PatternBuffer.Xor(buffer) cannot be null.");
            if (buffer.Size != this.Size)
                throw new ArgumentException("Operand buffer PatternBuffer.Xor(buffer) has wrong Size=" + buffer.Size.ToString() + ", this.Size=" + this.Size.ToString());
            this._ModifyContent((a, b) => (a == b ? '0' : '1'), buffer);        // Takhle vypadá výpočet XOR pro dva znaky char.
        }
        /// <summary>
        /// Modifikuje obsah this pomocí dodané funkce (operation) a dat druhého bufferu
        /// </summary>
        /// <param name="operation">Operace, která ze dvou dodaných znaků (první je z this, druhý je z buffer) vrátí výsledek, který se uloží do this</param>
        /// <param name="buffer">Druhý buffer, musí mít shodnou délku</param>
        private void _ModifyContent(Func<char, char, char> operation, PatternBuffer buffer)
        {
            StringBuilder bb = buffer.BufferBuilder;       // V případě potřeby vytvořím builder (a zruším buffer._BufferString)
            // Modifikuji obsah this zdejším výrazem:
            //  pro každý znak z this (c) na daném indexu (i) zavolám dodanou operaci (operation), 
            //  které předám znak c (=z this) a znak z dodaného bufferu na jeho indexu [i], což je shodný index našeho znaku:
            this._ModifyContent((c, i) => operation(c, bb[i]));
        }
        /// <summary>
        /// Modifikuje obsah this pomocí dodané funkce (operation)
        /// </summary>
        /// <param name="operation">Operace, která ze dodaného znaku (pochází z this) a indexu (tohoto znaku) 
        /// vrátí upravený výsledek (znak), který se uloží do this na původní místo</param>
        private void _ModifyContent(Func<char, int, char> operation)
        {
            StringBuilder sb1 = this.BufferBuilder;        // V případě potřeby vytvořím builder, zruším String
            int size = sb1.Length;
            for (int i = 0; i < size; i++)
                sb1[i] = operation(sb1[i], i);             // sb1 je živá reference na interní objekt this._BufferBuilder, změněný znak se ukládá přímo dovnitř.
        }
        #endregion
        #region Další odvozené property
        /// <summary>
        /// Velikost bufferu v bitech = počet znaků v bufferu. Aktuální, nijak nezarovnaná hodnota.
        /// </summary>
        internal int Size
        {
            get
            {
                if (this._BufferBuilder != null) return this._BufferBuilder.Length;   // Builder má přednost: pokud není NULL, pak je živý (kdežto String je jen záložní kopie)
                if (this._BufferString != null) return this._BufferString.Length;
                return 0;
            }
        }
        /// <summary>
        /// Size of this buffer in Bytes. Last byte is count (i.e. if Size = 10, then SizeInByte = 2).
        /// SizeInByte = Ceiling(Size / 8m).
        /// </summary>
        internal int SizeInByte { get { return (int)Math.Ceiling((decimal)this.Size / 8m); } }
        /// <summary>
        /// Count of unused bits in last used Byte.
        /// I.e. if Size = 10 bits, then NotAlignedBiteCount = 6 (=6 bits in last Byte is not used).
        /// If last Byte is all used, then NotAlignedBiteCount = 0 (i.e. if Size = 24).
        /// </summary>
        internal int NotAlignedBiteCount { get { return 8 * this.SizeInByte - this.Size; } }
        /// <summary>
        /// Current content of buffer.
        /// </summary>
        internal string Text
        {
            get
            {
                if (this._BufferString != null) return this._BufferString;             // String má přednost: pokud není NULL, pak obsahuje buď živý text, nebo posledně platnou kopii Builderu.
                if (this._BufferBuilder != null) return this._BufferBuilder.ToString();
                return null;
            }
        }
        #endregion
        #region Static služby - tvorba patternu z čísla
        /// <summary>
        /// Vytvoří a vrátí string, který obsahuje danou numerickou hodnotu (value) převedenou na pattern dané délky (length).
        /// Převádí text v pořadí, kdy nejnižší bit z (value) je na pozici vpravo, nejvyšší bit je vlevo (na indexu [0]).
        /// Například hodnotu 108d = 0x6C převede na pattern délky 8 bitů do stringu: "01101100".
        /// </summary>
        /// <param name="value"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        internal static string CreatePattern(int value, int length)
        {
            string errorMsg = "CreatePattern() error for value=" + value.ToString() + ", length=" + length.ToString() + ": value exceeds given length, max value for length = " + Math.Pow(2, length).ToString();
            return CreatePattern(value, length, errorMsg);
        }
        /// <summary>
        /// Vytvoří a vrátí string, který obsahuje danou numerickou hodnotu (value) převedenou na pattern dané délky (length).
        /// Převádí text v pořadí, kdy nejnižší bit z (value) je na pozici vpravo, nejvyšší bit je vlevo (na indexu [0]).
        /// Například hodnotu 108d = 0x6C převede na pattern délky 8 bitů do stringu: "01101100".
        /// </summary>
        /// <param name="value"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        internal static string CreatePattern(int value, int length, string errorMsg)
        {
            if (length <= 0)
                throw new ArgumentOutOfRangeException("length", "CreatePattern() error: argument \"length\" must be a positive number, current length=" + length.ToString() + ".");

            string pattern = "";
            int shift = value;
            for (int i = 0; i < length; i++)
            {
                pattern = ((shift & 0x01) > 0 ? "1" : "0") + pattern;      // Here is core for encoding numeric to pattern (bit order and bit representation)
                shift = shift >> 1;
            }

            if (shift != 0)
                throw new ArgumentOutOfRangeException("length", errorMsg);

            return pattern;
        }
        #endregion
        #region Extract bytes as Int32 array
        /// <summary>
        /// Create an array of int, where each Int32 is created from 8bit of data, starting at byte index [pointer].
        /// Whence data is organized as bits, then starting bit-index will be = 8 * (pointer).
        /// </summary>
        /// <param name="pointer"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        internal int[] ToByteArray(int pointer, int length)
        {
            int[] result = new int[length];

            string text = this.BufferString;
            int textLen = text.Length;
            int oneLen = 8;
            int textPointer = pointer * oneLen;
            int resultPointer = 0;
            while (textPointer < textLen && resultPointer < length)
            {
                string one = text.Substring(textPointer, oneLen);
                result[resultPointer] = CreateInt32(one);
                textPointer += oneLen;
                resultPointer++;
            }
            return result;
        }
        /// <summary>
        /// Convert binary coded text to Int32.
        /// Input should have the shape: "00010111" (this is 0x17), the result then will be = 23d.
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        private int CreateInt32(string text)
        {
            int value = 0;
            int len = text.Length;
            int pointer = len - 1;
            int exp = 1;
            while (pointer >= 0)
            {
                if (text[pointer] == '1')
                    value += exp;
                exp = 2 * exp;
                pointer--;
            }
            return value;
        }
        #endregion
    }
    #endregion
    #region class CodeMatrix: Bit matrix of data (similar with bitmap)
    /// <summary>
    /// CodeMatrix: Bit matrix of data (similar with bitmap).
    /// Contain only array of cells (of char).
    /// </summary>
    public partial class CodeMatrix : IDisposable
    {
        #region Konstrukce
        /// <summary>
        /// Constructor for empty matrix
        /// </summary>
        /// <param name="width"></param>
        /// <param name="height"></param>
        internal CodeMatrix(int width, int height)
        {
            this._Width = width;
            this._Height = height;
            this.Clear();
        }
        /// <summary>
        /// Constructor for matrix filled from data content.
        /// Data format: contain chars, which will be filled into array.
        /// Lines is separated with char ';'.
        /// Matrix is filled from top-left (X=0, Y=0).
        /// Each line of data is stored to one Y-line.
        /// Matrix has height = count of lines (separated ';'), and width equal to longest line in data.
        /// </summary>
        /// <param name="data"></param>
        internal CodeMatrix(string data)
        {
            string[] lines = data.Split(";".ToCharArray(), StringSplitOptions.None);
            int height = lines.Length;
            int width = lines.Max(l => l.Length);
            this._Width = width;
            this._Height = height;
            this.Clear();
            for (int y = 0; y < height; y++)
                this.Set(0, y, lines[y]);
        }
        private int _Width;
        private int _Height;
        private char[,] _Array;
        /// <summary>
        /// Vizualizace
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return this.Text;
        }
        void IDisposable.Dispose()
        {
            this._Array = null;
            this._CodeLabel = null;
        }
        #endregion
        #region Property, indexer, internal metody
        /// <summary>
        /// Width of CodeMatrix (0-dimension)
        /// </summary>
        public int Width { get { return this._Width; } }
        /// <summary>
        /// Height of CodeMatrix (1-dimension)
        /// </summary>
        public int Height { get { return this._Height; } }
        /// <summary>
        /// Content of this code.
        /// Only descriptor.
        /// </summary>
        public string Content { get; set; }
        /// <summary>
        /// Type of code, i.e. "QR code", "EAN-13", "UPC-A", a.s.o.
        /// Only descriptor.
        /// </summary>
        public string CodeType { get; set; }
        /// <summary>
        /// Version of code, depend on CodeType. I.e.: "V1.4" (QR code version 1, pattern 4)
        /// </summary>
        public string CodeVersion { get; set; }
        /// <summary>
        /// Character for empty cells
        /// </summary>
        public const char Empty = ' ';
        /// <summary>
        /// Get or set value (of Type: char) from/to specified cell (x, y).
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        public char this[int x, int y]
        {
            get
            {
                return this._Array[x, y];
            }
            set
            {
                this._Array[x, y] = value;
            }
        }
        /// <summary>
        /// Get or set value (of Type: char) from/to specified cell (x, y).
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        internal char this[Coord coord]
        {
            get
            {
                return this._Array[coord.X, coord.Y];
            }
            set
            {
                this._Array[coord.X, coord.Y] = value;
            }
        }
        /// <summary>
        /// Contain "debug" text of this matrix (text with (Width) columns and (Height) rows, filled with appropriate value).
        /// </summary>
        internal string Text
        {
            get
            {
                StringBuilder sb = new StringBuilder();
                this.ForEach((x, y) => sb.Append(this._Array[x, y].ToString() + " "), y => sb.AppendLine());
                return sb.ToString();
            }
        }
        /// <summary>
        /// Return true, if point at [x,y] is empty.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        internal bool IsEmptyPoint(int x, int y)
        {
            if (!IsValidCoordinates(x, y))
                throw new ArgumentOutOfRangeException("xy", "CodeMatrix: coordinates is not valid.");
            return (this._Array[x, y] == Empty);
        }
        /// <summary>
        /// Clear whole matrix (fill it with value = 0)
        /// </summary>
        internal void Clear()
        {
            this._Array = new char[_Width, _Height];
            ForEach((x, y) => this._Array[x, y] = Empty);
        }
        /// <summary>
        /// Scan all cells, and call method (actionOnPoint) for every adress (x, y).
        /// </summary>
        /// <param name="actionOnPoint">Action called for every cell (x, y)</param>
        internal void ForEach(Action<int, int> actionOnPoint)
        {
            this._ForEachRows(actionOnPoint, null);
        }
        /// <summary>
        /// Scan all cells, and call method (actionOnPoint) for every adress (x, y),
        /// and after scan whole every row (all dimension X in each one-to-one dimension y) call method (actionAfterRow) for row (y).
        /// </summary>
        /// <param name="actionOnPoint">Action called for every cell (x, y)</param>
        /// <param name="actionAfterRow">Action called after scan row (y)</param>
        internal void ForEach(Action<int, int> actionOnPoint, Action<int> actionAfterRow)
        {
            this._ForEachRows(actionOnPoint, actionAfterRow);
        }
        /// <summary>
        /// Scan all cells, and call method (actionOnPoint) for every adress (x, y).
        /// </summary>
        /// <param name="actionOnPoint">Action called for every cell (x, y)</param>
        internal void ForEach(ScanCellsOrder order, Action<int, int> actionOnPoint)
        {
            this.ForEach(order, actionOnPoint, null);
        }
        /// <summary>
        /// Scan all cells, and call method (actionOnPoint) for every adress (x, y),
        /// and after scan whole every row (all dimension X in each one-to-one dimension y) call method (actionAfterRow) for row (y).
        /// </summary>
        /// <param name="actionOnPoint">Action called for every cell (x, y)</param>
        /// <param name="actionAfterRowColumn">Action called after scan row (y)</param>
        internal void ForEach(ScanCellsOrder order, Action<int, int> actionOnPoint, Action<int> actionAfterRowColumn)
        {
            switch (order)
            {
                case ScanCellsOrder.Rows:
                    this._ForEachRows(actionOnPoint, actionAfterRowColumn);
                    break;
                case ScanCellsOrder.Columns:
                    this._ForEachColumns(actionOnPoint, actionAfterRowColumn);
                    break;
            }
        }
        /// <summary>
        /// Scan all cells, and call method (actionOnPoint) for every adress (x, y),
        /// and after scan whole every row (all dimension X in each one-to-one dimension y) call method (actionAfterRow) for row (y).
        /// </summary>
        /// <param name="actionOnPoint">Action called for every cell (x, y)</param>
        /// <param name="actionAfterRow">Action called after scan row (y)</param>
        private void _ForEachRows(Action<int, int> actionOnPoint, Action<int> actionAfterRow)
        {
            int w = this._Width;
            int h = this._Height;
            for (int y = 0; y < h; y++)
            {
                for (int x = 0; x < w; x++)
                {
                    if (actionOnPoint != null)
                        actionOnPoint(x, y);
                }
                if (actionAfterRow != null)
                    actionAfterRow(y);
            }
        }
        /// <summary>
        /// Scan all cells, and call method (actionOnPoint) for every adress (x, y),
        /// and after scan whole every row (all dimension X in each one-to-one dimension y) call method (actionAfterRow) for row (y).
        /// </summary>
        /// <param name="actionOnPoint">Action called for every cell (x, y)</param>
        /// <param name="actionAfterColumns">Action called after scan row (y)</param>
        private void _ForEachColumns(Action<int, int> actionOnPoint, Action<int> actionAfterColumns)
        {
            int w = this._Width;
            int h = this._Height;
            for (int x = 0; x < w; x++)
            {
                for (int y = 0; y < h; y++)
                {
                    if (actionOnPoint != null)
                        actionOnPoint(x, y);
                }
                if (actionAfterColumns != null)
                    actionAfterColumns(x);
            }
        }
        /// <summary>
        /// Order for scan cells in CodeMatrix
        /// </summary>
        internal enum ScanCellsOrder
        {
            /// <summary>
            /// By row (one whole row from left to right, then next row...)
            /// </summary>
            Rows,
            /// <summary>
            /// By columns (one whole column from top to bottom, then next column...)
            /// </summary>
            Columns
        }
        /// <summary>
        /// Return true, if coordinates (x, y) is valid.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns></returns>
        internal bool IsValidCoordinates(int x, int y)
        {
            return (x >= 0 && x < _Width && y >= 0 && y < _Height);
        }
        #endregion
        #region Set values
        /// <summary>
        /// Set into this matrix character from line, begin on position x,y.
        /// Old values will be overwritten.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="line"></param>
        internal void Set(int x, int y, string line)
        {
            this.MergeWith(x, y, line, QMatrixMergeMode.Override);
        }
        /// <summary>
        /// Set into this matrix values from other matrix, begin on position x,y.
        /// Old values will be overwritten.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="line"></param>
        internal void Set(int x, int y, CodeMatrix matrix)
        {
            this.MergeWith(x, y, matrix, QMatrixMergeMode.Override);
        }
        /// <summary>
        /// Set into this matrix values from other matrix, begin on position x,y.
        /// Old values will be overwritten.
        /// </summary>
        /// <param name="coord"></param>
        /// <param name="matrix"></param>
        internal void Set(Coord coord, CodeMatrix matrix)
        {
            this.MergeWith(coord.X, coord.Y, matrix, QMatrixMergeMode.Override);
        }
        /// <summary>
        /// Set into this matrix values from other matrix, begin on position x,y.
        /// Old values will be overwritten.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="line"></param>
        internal void Set(int x, int y, CodeMatrix matrix, bool copyDescriptions)
        {
            this.MergeWith(x, y, matrix, QMatrixMergeMode.Override);
            if (copyDescriptions)
                this.FillDescriptionsFrom(matrix);
        }
        /// <summary>
        /// Set into this matrix values from other matrix, begin on position x,y.
        /// Old values will be overwritten.
        /// </summary>
        /// <param name="coord"></param>
        /// <param name="matrix"></param>
        internal void Set(Coord coord, CodeMatrix matrix, bool copyDescriptions)
        {
            this.MergeWith(coord.X, coord.Y, matrix, QMatrixMergeMode.Override);
            if (copyDescriptions)
                this.FillDescriptionsFrom(matrix);
        }
        /// <summary>
        /// Merge into this matrix data from line, with specified mode.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="line"></param>
        /// <param name="mergeMode"></param>
        internal void MergeWith(int x, int y, string line, QMatrixMergeMode mergeMode)
        {
            for (int p = 0; p < line.Length; p++)
                this._Merge(x + p, y, line[p], mergeMode);
        }
        /// <summary>
        /// Merge into this matrix data from other matrix, with specified mode.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="matrix"></param>
        /// <param name="mergeMode"></param>
        internal void MergeWith(int x, int y, CodeMatrix matrix, QMatrixMergeMode mergeMode)
        {
            matrix.ForEach((a, b) => this._Merge(x + a, y + b, matrix._Array[a, b], mergeMode));
        }
        /// <summary>
        /// Fill Descriptions from other matrix
        /// </summary>
        /// <param name="source"></param>
        public void FillDescriptionsFrom(CodeMatrix source)
        {
            this.Content = source.Content;
            this.CodeType = source.CodeType;
            this.CodeVersion = source.CodeVersion;
        }
        /// <summary>
        /// Merge into specified point (x,y) char (c), with mode (mergeMode).
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="c"></param>
        /// <param name="mergeMode"></param>
        private void _Merge(int x, int y, char c, QMatrixMergeMode mergeMode)
        {
            if (this.IsValidCoordinates(x, y))
            {
                switch (mergeMode)
                {
                    case QMatrixMergeMode.Override:
                        this._Array[x, y] = c;
                        break;
                    case QMatrixMergeMode.SetIfEmpty:
                        if (this._Array[x, y] == Empty)
                            this._Array[x, y] = c;
                        break;
                }
            }
        }
        #endregion
        #region Extract
        /// <summary>
        /// Create text from this, CodeMatrix, from specified area of matrix.
        /// Returning string has form of continuous character from each extracted row, separated with ';' from next line[s].
        /// Separator can be specified in (optional) fifth parameters.
        /// </summary>
        /// <param name="x">Coordinate X, where extraction beginning</param>
        /// <param name="y">Coordinate Y, where extraction beginning</param>
        /// <param name="width">Number of cell in dimension X to extract</param>
        /// <param name="height">Number of cell in dimension Y to extract</param>
        /// <returns></returns>
        internal string Extract(int x, int y, int width, int height)
        {
            return this.Extract(x, y, width, height, ";");
        }
        /// <summary>
        /// Create text from this, CodeMatrix, from specified area of matrix.
        /// Returning string has form of continuous character from each extracted row, separated with ';' from next line[s].
        /// Separator can be specified in (optional) fifth parameters.
        /// </summary>
        /// <param name="x">Coordinate X, where extraction beginning</param>
        /// <param name="y">Coordinate Y, where extraction beginning</param>
        /// <param name="width">Number of cell in dimension X to extract</param>
        /// <param name="height">Number of cell in dimension Y to extract</param>
        /// <param name="lineSeparator">Separator of lines</param>
        /// <returns></returns>
        internal string Extract(int x, int y, int width, int height, string lineSeparator)
        {
            StringBuilder sb = new StringBuilder();
            int right = x + width;
            int bottom = y + height;
            int lastLine = bottom - 1;
            for (int line = y; line < bottom; line++)                // lines (Y)
            {
                for (int column = x; column < right; column++)       // columns (X)
                    sb.Append(IsValidCoordinates(column, line) ? this._Array[column, line] : Empty);
                if (lineSeparator != null && line < lastLine)
                    sb.Append(lineSeparator);
            }
            return sb.ToString();
        }
        #endregion
        #region Transform
        /// <summary>
        /// Return new CodeMatrix, created from this CodeMatrix with specified transformation mode.
        /// </summary>
        /// <param name="transformMode"></param>
        /// <returns></returns>
        internal CodeMatrix CreateTransform(QMatrixTransformMode transformMode)
        {
            bool rotate = (transformMode == QMatrixTransformMode.SwapXY);     // Create new CodeMatrix with flipped size (Width <=> Height)
            int w = (!rotate ? this.Width : this.Height);
            int h = (!rotate ? this.Height : this.Width);
            CodeMatrix matrix = new CodeMatrix(w, h);
            switch (transformMode)
            {
                case QMatrixTransformMode.None:
                    this.ForEach((x, y) => this._TransformPointNone(x, y, matrix));
                    break;
                case QMatrixTransformMode.SwapXY:
                    this.ForEach((x, y) => this._TransformPointSwapXY(x, y, matrix));
                    break;
                default:
                    throw new ArgumentException("CodeMatrix.CreateTransform() can not transform for mode=" + transformMode.ToString(), "transformMode");
            }
            return matrix;
        }
        /// <summary>
        /// Transform one point from [x,y] into target matrix for mode None (store content into coordinate [x,y]).
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="matrix"></param>
        private void _TransformPointNone(int x, int y, CodeMatrix matrix)
        {
            matrix[x, y] = this._Array[x, y];
        }
        /// <summary>
        /// Transform one point from [x,y] into target matrix for mode SwapXY (store content into coordinate [y,x]).
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="matrix"></param>
        private void _TransformPointSwapXY(int x, int y, CodeMatrix matrix)
        {
            matrix[y, x] = this._Array[x, y];
        }
        #endregion
        #region CodeLabel
        /// <summary>
        /// Label with position of each character. Can be a null.
        /// </summary>
        public CodeLabel CodeLabel { get { return this._CodeLabel; } }
        private CodeLabel _CodeLabel;
        /// <summary>
        /// Clear whole CodeLabel
        /// </summary>
        internal void CodeLabelClear()
        {
            this._CodeLabel = null;
        }
        /// <summary>
        /// Add one character to CodeLabel
        /// </summary>
        /// <param name="text"></param>
        /// <param name="bounds"></param>
        internal void CodeLabelAdd(char text, Rectangle bounds)
        {
            if (this._CodeLabel == null)
                this._CodeLabel = new CodeLabel();
            this._CodeLabel.Add(text, bounds);
        }
        /// <summary>
        /// Add range of CodeLabelItem into this.CodeLabel, with shift of each character
        /// </summary>
        /// <param name="source"></param>
        /// <param name="shiftX"></param>
        /// <param name="shiftY"></param>
        internal void CodeLabelAddRange(CodeLabel source, int shiftX, int shiftY)
        {
            if (this._CodeLabel == null)
                this._CodeLabel = new CodeLabel();
            this._CodeLabel.AddRange(source, shiftX, shiftY);
        }
        #endregion
        #region PaintTo() : vykreslí matrix do grafiky
        /// <summary>
        /// Vykreslí daný kód skrz danou grafiku do uvedeného prostoru, v daných barvách.
        /// Vrací true = OK / false = chyba (typicky malý prostor).
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="area"></param>
        /// <param name="colorLight"></param>
        /// <param name="colorDark"></param>
        /// <returns></returns>
        public bool PaintCodeTo(Graphics graphics, Rectangle area, Color colorLight, Color colorDark, Color colorLabel, bool labelVisible)
        {
            Size clientSize = area.Size;
            if (clientSize.Width < 10 || clientSize.Height < 10) return false;

            // Výpočet velikosti jednoho bodu (modul) - jeden čtvereček odpovídá jednomu logickému bodu, vykresluje se na potřebný ucelený (Int32, ne Float) počet fyzických pixelů:
            int matrixWidth = this.Width;
            int matrixHeight = this.Height;
            int moduleWidth = clientSize.Width / matrixWidth;            // Dělení v Int32 plně vyhovuje účelu: najde nejnižší celý podíl, ve směru X i Y
            int moduleHeight = clientSize.Height / matrixHeight;
            int moduleDim = (moduleWidth < moduleHeight ? moduleWidth : moduleHeight);   // Menší z hodnot ve směru X a Y
            if (moduleDim <= 0) return false;

            // Určení prostoru, který reálně obsadí kód:
            Size codeSize = new Size(matrixWidth * moduleDim, matrixHeight * moduleDim);
            int x = (clientSize.Width - codeSize.Width) / 2;
            int y = (clientSize.Height - codeSize.Height) / 2;
            Rectangle codeArea = new Rectangle(new Point(area.X + x, area.Y + y), codeSize);

            // Kreslení kódu:
            using (SolidBrush brush = new SolidBrush(colorLight))
            {
                if (!colorLight.IsEmpty)
                    graphics.FillRectangle(brush, codeArea);

                brush.Color = colorDark;

                int i = 0;  // Logická dimenze ve směru X
                int j = 0;  // Logická dimenze ve směru Y
                for (int fy = 0; fy < codeSize.Height; fy += moduleDim)         // Fyzická souřadnice Y jde odshora, v krocích podle velikosti "bodu" (moduleDim)
                {
                    i = 0;
                    for (int fx = 0; fx < codeSize.Width; fx += moduleDim)      // Fyzická souřadnice X jde zleva doprava, krok = velikost modulu (moduleDim)
                    {
                        if (this[i, j] == '1')
                        {   // Kreslit budu jen černé body:
                            Rectangle point = new Rectangle(codeArea.X + fx, codeArea.Y + fy, moduleDim, moduleDim);
                            graphics.FillRectangle(brush, point);
                        }
                        i++;
                    }
                    j++;
                }

                // Kreslení labelu:
                if (labelVisible && this._CodeLabel != null)
                {
                    brush.Color = colorLabel;
                    this.PaintLabelTo(graphics, codeArea, moduleDim, brush);
                }
            }

            return true;
        }
        /// <summary>
        /// Draw label to graphics
        /// </summary>
        /// <param name="graphics"></param>
        /// <param name="codeArea"></param>
        /// <param name="moduleDim"></param>
        /// <param name="brush"></param>
        private void PaintLabelTo(Graphics graphics, Rectangle codeArea, int moduleDim, SolidBrush brush)
        {
            Size? labelSize = this._CodeLabel.MinimalSize;
            if (!labelSize.HasValue) return;

            var state = graphics.Save();
            graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
            StringFormat sf = new StringFormat(StringFormatFlags.NoWrap | StringFormatFlags.FitBlackBox | StringFormatFlags.LineLimit);

            float emSize = 0.70f * (float)(labelSize.Value.Height * moduleDim);
            using (Font font = new Font(FontFamily.GenericMonospace, emSize, FontStyle.Bold, GraphicsUnit.Pixel))
            {
                foreach (var item in this._CodeLabel.Items)
                {
                    string text = item.Text.ToString();
                    RectangleF area = item.GetPhysicalBound(codeArea, moduleDim);
                    SizeF size = graphics.MeasureString(text, font);
                    if (size.Width <= area.Width && size.Height <= area.Height)
                    {   // Center text into area:
                        PointF point = new PointF(area.X + 0.5f * (area.Width - size.Width), area.Y + 0.5f * (area.Height - size.Height));
                        graphics.DrawString(text, font, brush, point);
                    }
                    else
                    {   // Clip text into area:
                        graphics.DrawString(text, font, brush, area, sf);
                    }
                }
            }
            graphics.Restore(state);
        }
        #endregion
        #region Export QR kódu do bitmapy
        /// <summary>
        /// Return optimal bitmap size for parameters
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        public Size GetBitmapSize(CodeProperties properties)
        {
            return this.GetBitmapSize(properties.ModuleSize, properties.ImageWidth, properties.ImageHeight, properties.Orientation);
        }
        /// <summary>
        /// Return optimal bitmap size for parameters
        /// </summary>
        /// <param name="moduleSize"></param>
        /// <param name="width"></param>
        /// <param name="height"></param>
        /// <param name="orientation"></param>
        /// <returns></returns>
        public Size GetBitmapSize(Int32? moduleSize, Int32? width, Int32? height, CodeOrientation orientation)
        {
            int w = 0;
            int h = 0;
            if (width.HasValue && width.Value > 0 && height.HasValue && height.Value > 0)
            {   // Valid width and height:
                w = width.Value;
                h = height.Value;
            }
            else if (moduleSize.HasValue && moduleSize.Value > 0)
            {   // Valid moduleSize:
                w = this.Width * moduleSize.Value;
                h = this.Height * moduleSize.Value;
            }
            else
            {   // Default moduleSize = 4px / point:
                w = this.Width * 4;
                h = this.Height * 4;
            }

            if (_IsOrientation90(orientation))
                return new Size(h, w);
            return new Size(w, h);
        }
        /// <summary>
        /// Vrátí obrázek obsahující tento QR kód, vykreslený v dané velikosti bitmapy a s daným rozlišením (jde jen o údaj o rozlišení, nemá vliv na skutečnou velikost čtverečku kódu).
        /// Lze zadat barvy okolí kódu (běžně šedá), barvu podkladu (běžně bílá) a barvu kódu (běžně černá).
        /// </summary>
        /// <param name="size">Velikost bitmapy v pixelech. Namísto velikosti je možno zadat počet pixelů na jeden datový bod (čtvereček).</param>
        /// <param name="resolutionDPI">Rozlišení bitmapy, určuje fyzickou velikost v mm (kdežto zadaná velikost size je v pixelech)</param>
        /// <param name="grayColor">Barva okolí kódu, výchozí je Color.LightGray</param>
        /// <param name="whiteColor">Barva pozadí kódu, výchozí je Color.White</param>
        /// <param name="blackColor">Barva kódu, výchozí je Color.Black</param>
        /// <returns></returns>
        public Bitmap ToBitmap(Size size, float resolutionDPI, Color grayColor, Color whiteColor, Color blackColor, Color labelColor, bool labelVisible, CodeOrientation orientation)
        {
            CodeProperties properties = new CodeProperties() { ImageWidth = size.Width, ImageHeight = size.Height, GrayColor = grayColor, WhiteColor = whiteColor, BlackColor = blackColor, LabelColor = labelColor, LabelVisible = labelVisible, Orientation = orientation };
            return this._ToBitmap(properties);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="properties"></param>
        /// <returns></returns>
        public Bitmap ToBitmap(CodeProperties properties)
        {
            return this._ToBitmap(properties);
        }
        private Bitmap _ToBitmap(CodeProperties properties)
        {
            Size size = this.GetBitmapSize(properties);
            Bitmap bmp = new Bitmap(size.Width, size.Height, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            bmp.SetResolution(properties.Resolution, properties.Resolution);

            Rectangle area = new Rectangle(0, 0, size.Width, size.Height);
            using (Graphics graphics = Graphics.FromImage(bmp))
            {
                graphics.Clear(properties.GrayColor);
                graphics.Transform = CreateMatrix2DForOrientation(properties.Orientation, area);
                this.PaintCodeTo(graphics, area, properties.WhiteColor, properties.BlackColor, properties.LabelColor, properties.LabelVisible);
            }

            return bmp;
        }
        /// <summary>
        /// Return true, if orientation is "by 90/270 degrees" rotated (width is transformed to Y axis, and height is to X axis).
        /// </summary>
        /// <param name="orientation"></param>
        /// <returns></returns>
        private bool _IsOrientation90(CodeOrientation orientation)
        {
            return ((orientation & CodeOrientation.Rotate90) != 0);         // true => width of code is height of image
        }
        /// <summary>
        /// Return a Drawing2D.Matrix, which will be transform logical coordinates to graphics
        /// </summary>
        /// <param name="orientation"></param>
        /// <param name="area"></param>
        /// <returns></returns>
        private System.Drawing.Drawing2D.Matrix CreateMatrix2DForOrientation(CodeOrientation orientation, RectangleF area)
        {
            System.Drawing.Drawing2D.Matrix mx = new System.Drawing.Drawing2D.Matrix();
            if (orientation == CodeOrientation.Regular) return mx;                                     // No transformation

            // Rotation:
            PointF center = new PointF(area.X + area.Width / 2f, area.Y + area.Height / 2f);
            CodeOrientation rotate = (CodeOrientation)(orientation & CodeOrientation.Rotate270);       // 0,1,2,3 = rotate 0-90-180-270°
            switch (rotate)
            {
                case CodeOrientation.Rotate90:
                    mx.RotateAt(90f, center);
                    break;
                case CodeOrientation.Rotate180:
                    mx.RotateAt(180f, center);
                    break;
                case CodeOrientation.Rotate270:
                    mx.RotateAt(270f, center);
                    break;
            }

            // Mirroring:
            bool mirrorX = ((orientation & CodeOrientation.MirrorX) != 0);
            bool mirrorY = ((orientation & CodeOrientation.MirrorY) != 0);
            if (mirrorX || mirrorY)
            {
                mx.Scale((mirrorX ? -1f : 1f), (mirrorY ? -1f : 1f), System.Drawing.Drawing2D.MatrixOrder.Append);
                mx.Translate((mirrorX ? area.Width : 0f), (mirrorY ? area.Height : 0f), System.Drawing.Drawing2D.MatrixOrder.Append);
            }

            return mx;
        }
        /// <summary>
        /// Vrátí System.Drawing.Imaging.ImageFormat odpovídající příponě jména souboru.
        /// Pracuje pouze pro přípony BMP, FIG, JPG, JPEG, PNG, TIF, TIFF. Implicitní je GIF.
        /// Ostatní formáty nevrací, protože nejsou kompatibilní s kreslením pixelů pomocí třídy Bitmap.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static System.Drawing.Imaging.ImageFormat GetImageFormatFromName(string name)
        {
            string ext = System.IO.Path.GetExtension(name).ToLower();
            switch (ext)
            {
                case ".bmp": return System.Drawing.Imaging.ImageFormat.Bmp;
                // case ".emf": return System.Drawing.Imaging.ImageFormat.Emf;
                // case ".exif": return System.Drawing.Imaging.ImageFormat.Exif;
                case ".gif": return System.Drawing.Imaging.ImageFormat.Gif;
                // case ".ico": return System.Drawing.Imaging.ImageFormat.Icon;
                case ".jpg": return System.Drawing.Imaging.ImageFormat.Jpeg;
                case ".jpeg": return System.Drawing.Imaging.ImageFormat.Jpeg;
                case ".png": return System.Drawing.Imaging.ImageFormat.Png;
                case ".tif": return System.Drawing.Imaging.ImageFormat.Tiff;
                case ".tiff": return System.Drawing.Imaging.ImageFormat.Tiff;
                // case ".wmf": return System.Drawing.Imaging.ImageFormat.Wmf;
            }
            return System.Drawing.Imaging.ImageFormat.Gif;
        }
        #endregion
    }
    /// <summary>
    /// Mode for Merge values into CodeMatrix
    /// </summary>
    internal enum QMatrixMergeMode
    {
        /// <summary>Not specified</summary>
        None,
        /// <summary>Override any current value</summary>
        Override,
        /// <summary>Set only if current cell of matrix is Empty</summary>
        SetIfEmpty
    }
    /// <summary>
    /// Mode for Transform of CodeMatrix
    /// </summary>
    internal enum QMatrixTransformMode
    {
        /// <summary>
        /// No transform
        /// </summary>
        None,
        /// <summary>
        /// Swap X and Y coordinates
        /// </summary>
        SwapXY
    }
    #endregion
    #region class Coord: coordinates of one point (X, Y)
    /// <summary>
    /// Coord: coordinates of one point (X, Y)
    /// </summary>
    internal class Coord
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        internal Coord(int x, int y)
        {
            this.X = x;
            this.Y = y;
        }
        /// <summary>
        /// Visualisation
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return "X=" + this.X.ToString() + "; Y=" + this.Y.ToString();
        }
        /// <summary>
        /// Cordinate X
        /// </summary>
        internal int X { get; private set; }
        /// <summary>
        /// Cordinate Y
        /// </summary>
        internal int Y { get; private set; }
    }
    #endregion
    #region class CodeLabel: exactly positioned label characters
    /// <summary>
    /// CodeLabel: exactly positioned label characters
    /// </summary>
    public class CodeLabel
    {
        public CodeLabel()
        {
            this._ItemList = new List<CodeLabelItem>();
        }
        private List<CodeLabelItem> _ItemList;
        public override string ToString()
        {
            return this.Text;
        }
        /// <summary>
        /// All characters
        /// </summary>
        public IEnumerable<CodeLabelItem> Items { get { return this._ItemList; } }
        /// <summary>
        /// Clear label
        /// </summary>
        public void Clear()
        {
            this._ItemList.Clear();
        }
        /// <summary>
        /// Add one character to end of label
        /// </summary>
        /// <param name="text"></param>
        /// <param name="bounds"></param>
        public void Add(char text, Rectangle bounds)
        {
            this._ItemList.Add(new CodeLabelItem(text, bounds));
        }
        /// <summary>
        /// Add range of CodeLabelItem into this.CodeLabel, with shift of each character
        /// </summary>
        /// <param name="source"></param>
        /// <param name="shiftX"></param>
        /// <param name="shiftY"></param>
        internal void AddRange(CodeLabel source, int shiftX, int shiftY)
        {
            foreach (CodeLabelItem item in source.Items)
                this._ItemList.Add(new CodeLabelItem(item.Text, new Rectangle(item.Bounds.X + shiftX, item.Bounds.Y + shiftY, item.Bounds.Width, item.Bounds.Height)));
        }
        /// <summary>
        /// Text of label (summa of characters).
        /// </summary>
        public string Text
        {
            get
            {
                string text = "";
                foreach (CodeLabelItem item in this._ItemList)
                    text += item.Text;
                return text;
            }
        }
        /// <summary>
        /// Minimal size of character. When Items is empty, then MinimalSize is null.
        /// </summary>
        public Size? MinimalSize
        {
            get
            {
                Int32? mw = null;
                Int32? mh = null;
                foreach (CodeLabelItem item in this._ItemList)
                {
                    if (!mw.HasValue || (mw.HasValue && item.Bounds.Width < mw.Value)) mw = item.Bounds.Width;
                    if (!mh.HasValue || (mh.HasValue && item.Bounds.Height < mh.Value)) mh = item.Bounds.Height;
                }
                if (mw.HasValue && mh.HasValue) return new Size(mw.Value, mh.Value);
                return null;
            }
        }
        /// <summary>
        /// CodeLabelItem: one character + its boundary
        /// </summary>
        public class CodeLabelItem
        {
            public CodeLabelItem(char text, Rectangle bounds)
            {
                this.Text = text;
                this.Bounds = bounds;
            }
            public override string ToString()
            {
                return this.Text.ToString();
            }
            /// <summary>
            /// One character of label
            /// </summary>
            public char Text { get; private set; }
            /// <summary>
            /// Bounds of character in matrix
            /// </summary>
            public Rectangle Bounds { get; private set; }
            /// <summary>
            /// Return current bound recalculated to specified physical coordinate.
            /// </summary>
            /// <param name="codeArea"></param>
            /// <param name="moduleDim"></param>
            /// <returns></returns>
            internal RectangleF GetPhysicalBound(Rectangle codeArea, int moduleDim)
            {
                Rectangle labelBound = this.Bounds;
                int x = codeArea.X + (labelBound.X * moduleDim);
                int y = codeArea.Y + (labelBound.Y * moduleDim);
                int w = labelBound.Width * moduleDim;
                int h = labelBound.Height * moduleDim;
                return new RectangleF(x, y, w, h);
            }
        }
    }
    #endregion
}
